#include "PrecompiledHeaders.h"
#include "GameResources.h"
#include "Mouse.h"

LevelVector GameResources::levels(0);
LevelVector GameResources::bonusLevels(0);
Level *GameResources::current_level = NULL;

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

static void RedirectIOToConsole(void)
{
    int hConHandle;
    long lStdHandle;
    
    CONSOLE_SCREEN_BUFFER_INFO coninfo;
    FILE *fp;
    
    // allocate a console for this app
    AllocConsole();
    
    // set the screen buffer to be big enough to let us scroll text
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),     &coninfo);
    coninfo.dwSize.Y = MAX_CONSOLE_LINES;
    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
    
    // redirect unbuffered STDOUT to the console
    lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "w" );
    *stdout = *fp;
    setvbuf( stdout, NULL, _IONBF, 0 );
    
    // redirect unbuffered STDIN to the console
    lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "r" );
    *stdin = *fp;
    setvbuf( stdin, NULL, _IONBF, 0 );
    
    // redirect unbuffered STDERR to the console
    lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "w" );
    *stderr = *fp;
    setvbuf( stderr, NULL, _IONBF, 0 );
}

#endif

GameResources::GameResources(bool pctl, bool pcfg)
{
    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    RedirectIOToConsole();
    #endif
    
    pod_control = pctl;
    pod_config_file = pcfg;
    
    createRoot();
    defineResources();
    loadConfiguration();
    defineLevels();
    LevelLocks::setLevelLocks(&levels);
    LevelLocks::unlockAll(&levels);
    setupRenderSystem();
    createRenderWindow();
    initializeResourceGroups();
    setupSceneManager();
    setupViewport();
    setupInputSystem();
    setupCameras();
    setupCEGUI();
    setupOgreNewt();
    setupAudio();
    skyBox = new SkyBox(this);
    loadHighScores();
    
    if (pod_control) {
        if(joystick) current_device = (InputDevice *)joystick;
        else current_device = (InputDevice *)mouse;
        current_camera = (CameraControl *)first_person_camera;
        pod = new PodServerInterface();
    } else {
        current_device = (InputDevice *)mouse;
        current_camera = (CameraControl *)third_person_camera;
        pod = NULL;
    }

    current_level = NULL;
}

GameResources::~GameResources()
{
    LevelLocks::saveLevelLocks(&levels);
    delete config;
    // delete levels
    if (pod != NULL) delete pod;
    // de-initialize the debugger.
    if(joystick) joystick->destroy(input_manager);
    if(mouse) mouse->destroy(input_manager);
    if(keyboard) keyboard->destroy(input_manager);
    OIS::InputManager::destroyInputSystem(input_manager);
    delete GUISystem;
    delete ogre;
    delete audio;
    delete scores;
}

void GameResources::createRoot()
{
    //Ogre::LogManager *log_mgr = 
    new Ogre::LogManager;
    //Ogre::Log *log = 
    Ogre::LogManager::getSingleton().createLog("Ogre.log", true, true, false);
    
    ogre = new Ogre::Root("","");
    
    #if defined(_DEBUG)
        #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            ogre->loadPlugin("RenderSystem_Direct3D9_d");
        #endif
        ogre->loadPlugin("RenderSystem_GL_d");
        ogre->loadPlugin("Plugin_OctreeSceneManager_d");
        ogre->loadPlugin("Plugin_ParticleFX_d");
    #else
        #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            ogre->loadPlugin("RenderSystem_Direct3D9");
        #endif
        ogre->loadPlugin("RenderSystem_GL");
        ogre->loadPlugin("Plugin_OctreeSceneManager");
        ogre->loadPlugin("Plugin_ParticleFX");
    #endif
}

void GameResources::defineResources()
{
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "menu/layouts", "FileSystem", "GUI");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "menu/imagesets", "FileSystem", "GUI");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "menu/other", "FileSystem", "GUI");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "fonts", "FileSystem", "GUI");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "materials", "FileSystem", "GUI");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "materials/particles", "FileSystem", "GUI");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "overlays", "FileSystem", "GUI");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "particles", "FileSystem", "General");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "meshes", "FileSystem", "General");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Constants::RESOURCES_FOLDER + "menu/layouts/embedded", "FileSystem", "GUI");
 }

void GameResources::loadConfiguration()
{
    config = new Configuration();
    if (pod_config_file)
        config->load(Constants::POD_CONFIG_FILE);
    else
        config->load(Constants::CONFIG_FILE);
}

/*
 * Add all the levels from the level.ini file.
 */
void GameResources::defineLevels()
{
    Ogre::String levelName;
    Ogre::ConfigFile cf;
    Ogre::String res_folder (Constants::RESOURCES_FOLDER);
    cf.load(res_folder + "levels/levels.ini");
    
    Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
    while (seci.hasMoreElements())
    {
        levelName = seci.peekNextKey();
        if (levelName == "") {
            seci.getNext();
        } else {
            Level level(levelName);
            try {
                level.init(seci.getNext());
                if(level.isBonus()) {
                    bonusLevels.push_back(level);
                } else {
                    levels.push_back(level);
                }
            } catch (Ogre::Exception e) {
                Ogre::LogManager::getSingleton().logMessage(e.getDescription());
            }
        }
    }
}

void GameResources::setupRenderSystem()
{              
    Ogre::RenderSystemList *renderSystems = NULL;
    Ogre::RenderSystemList::iterator r_it;
    
    renderSystems = ogre->getAvailableRenderers();
    
    // check through the list of available renderers, looking for the one that contains
    // the string in "val" ('renderSystem' option from the config.ini file)
    bool renderSystemFound = false;
    for (r_it=renderSystems->begin(); r_it!=renderSystems->end(); r_it++)
    {
        Ogre::RenderSystem *tmp = *r_it;
        // returns -1 if string not found
        if ((int) tmp->getName().find(config->getRenderSystem()) >= 0)
        {
            ogre->setRenderSystem(*r_it);
            renderSystemFound = true;
            break;
        }
    }
    
    if (!renderSystemFound) {
        throw Ogre::Exception(52, "Specified render system ("
            + config->getRenderSystem() + ") not found", "GameResources::setupRenderSystem");
    }
    
    ogre->initialise(false);
}

void GameResources::createRenderWindow()
{
    render_window = ogre->createRenderWindow("Totoro", config->getResolutionWidth(),
        config->getResolutionHeight(), config->isFullScreen(),
        config->getVideoOptions());
    //render_window->reposition(0, 0);
}

void GameResources::initializeResourceGroups()
{
    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

void GameResources::setupSceneManager()
{
    scene_manager = ogre->createSceneManager(Ogre::ST_GENERIC);
    scene_manager->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
}


void GameResources::setupViewport()
{
    if (config->isTriScreen())
    {
        Ogre::Viewport *left, *right, *main;
        if(pod_config_file) {
            left  = render_window->addViewport(NULL, 1, 0.0f, 0.0f, 1.0f/3, 0.8f); // left viewport
            main  = render_window->addViewport(NULL, 0, 1.0f/3, 0.0f, 1.0f/3, 0.8f); // center viewport
            right = render_window->addViewport(NULL, 2, 2.0f/3, 0.0f, 1.0f/3, 0.8f); // right viewport      
        }
        else {
            left  = render_window->addViewport(NULL, 1, 0.0f, 0.0f, 1.0f/3, 1.0f); // left viewport
            main  = render_window->addViewport(NULL, 0, 1.0f/3, 0.0f, 1.0f/3, 1.0f); // center viewport
            right = render_window->addViewport(NULL, 2, 2.0f/3, 0.0f, 1.0f/3, 1.0f); // right viewport
        }
        left->setOverlaysEnabled(false);
        right->setOverlaysEnabled(false);
    }
    else 
    {
        if(pod_config_file)
            render_window->addViewport(NULL, 0, 0.0f, 0.0f, 1.0f, 0.8f);
        else
            render_window->addViewport(NULL, 0, 0.0f, 0.0f, 1.0f, 1.0f);
    }
}

void GameResources::setupInputSystem()
{
    size_t windowHnd = 0;
    std::ostringstream windowHndStr;
    OIS::ParamList pl;
    
    render_window->getCustomAttribute("WINDOW", &windowHnd);
    windowHndStr << windowHnd;
    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
    input_manager = OIS::InputManager::createInputSystem(pl);
    
    try
    {
    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

        if (input_manager->numMice() > 0)
        {
            mouse = new Mouse(static_cast<OIS::Mouse*>(input_manager->createInputObject(OIS::OISMouse, true)), this);
            unsigned int width, height, depth;
            int left, top;
            render_window->getMetrics(width, height, depth, left, top);
            mouse->setWidth(width);
            mouse->setHeight(height);
        }
        else mouse = NULL;

        if (input_manager->numKeyboards() > 0)
            keyboard = new Keyboard(static_cast<OIS::Keyboard*>(input_manager->createInputObject(OIS::OISKeyboard, true)));
        else keyboard = NULL;

        if (input_manager->numJoySticks() > 0)
            joystick = new Joystick(static_cast<OIS::JoyStick*>(input_manager->createInputObject(OIS::OISJoyStick, true)));
        else joystick = NULL;

    #else

        if (input_manager->getNumberOfDevices(OIS::OISMouse) > 0)
        {
            mouse = new Mouse(static_cast<OIS::Mouse*>(input_manager->createInputObject(OIS::OISMouse, true)), this);
            unsigned int width, height, depth;
            int left, top;
            render_window->getMetrics(width, height, depth, left, top);
            mouse->setWidth(width);
            mouse->setHeight(height);
        }
        else mouse = NULL;

        if (input_manager->getNumberOfDevices(OIS::OISKeyboard) > 0)
            keyboard = new Keyboard(static_cast<OIS::Keyboard*>(input_manager->createInputObject(OIS::OISKeyboard, true)));
        else keyboard = NULL;

        if (input_manager->getNumberOfDevices(OIS::OISJoyStick) > 0)
            joystick = new Joystick(static_cast<OIS::JoyStick*>(input_manager->createInputObject(OIS::OISJoyStick, true)));
        else joystick = NULL;

    #endif
    }
    catch (const OIS::Exception &e)
    {
        throw new Ogre::Exception(42, e.eText, "Application::setupInputSystem");
    }
}

void GameResources::setupAudio()
{
    audio = new AudioInterface();
    // Set up default volumes
    audio->setVolumeBGM(config->getVolumeBGM());
    audio->setVolumeSFX(config->getVolumeSFX());
}

void GameResources::setupCameras()
{
    Ogre::Viewport *main = render_window->getViewport(0), *left, *right;
    main_gui_camera = scene_manager->createCamera("main_gui_camera");
    if(pod_config_file)
    {
        left = render_window->getViewport(1);
        right = render_window->getViewport(2);
        left_gui_camera = scene_manager->createCamera("left_gui_camera");
        right_gui_camera = scene_manager->createCamera("right_gui_camera");
    }
    else
    {
        left = NULL;
        right = NULL;
        left_gui_camera = NULL;
        right_gui_camera = NULL;
    }
    first_person_camera = new FirstPersonCamera(main, left, right, scene_manager);
    third_person_camera = new ThirdPersonCamera(main, left, right, scene_manager);
}

void GameResources::setupCEGUI()
{
    // CEGUI setup
    GUIRenderer = new CEGUI::OgreCEGUIRenderer(render_window,
        Ogre::RENDER_QUEUE_OVERLAY, false, 3000, scene_manager);
    GUISystem = new CEGUI::System(GUIRenderer);
    // Selects the skin - See CEGUI for customisation...
    CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme", (CEGUI::utf8*)"GUI");
    // Creates the imagesets which are to be used for the level selection menu
    CEGUI::ImagesetManager::getSingleton().createImageset("MenuBackground1.imageset");
	CEGUI::ImagesetManager::getSingleton().createImageset("MenuBackground2.imageset");
	CEGUI::ImagesetManager::getSingleton().createImageset("MenuBackground3.imageset");
    CEGUI::ImagesetManager::getSingleton().createImageset("MenuImages.imageset");
    CEGUI::ImagesetManager::getSingleton().createImageset("Levels.imageset");
    CEGUI::FontManager::getSingleton().createFont("finder-25.font");
    GUISystem->setDefaultFont((CEGUI::utf8*)"Finder-25");
    GUISystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow");
}

void GameResources::setupOgreNewt()
{
    //ogre_newt = new OgreNewt::World();
}

void GameResources::windowResized(Ogre::RenderWindow *rw)
{
    unsigned int width, height, depth;
    int left, top;
    rw->getMetrics(width, height, depth, left, top);
    if(mouse) mouse->setWidth(width);
    if(mouse) mouse->setHeight(height);
}

void GameResources::loadHighScores()
{
    scores = new HighScore();
}

